#pragma once
#include <atomic>
namespace xncc {
namespace msg {

#ifndef FORCE_INLINE
#    if defined(_MSC_VER)
#        define FORCE_INLINE __forceinline
#    elif defined(__GNUC__)
#        define FORCE_INLINE inline __attribute__((always_inline))
#    else
#        define FORCE_INLINE inline
#    endif
#endif

#define CACHE_LINE_SIZE 64

    class RingBuffer
    {
      public:
        // Allocate buffer space for writing.
        FORCE_INLINE void* PrepareWrite(std::size_t size, std::size_t alignment);

        // Publish written data.
        FORCE_INLINE void FinishWrite();

        // Write an element to the buffer.
        template <typename T>
        FORCE_INLINE void Write(const T& value)
        {
            void* dest = PrepareWrite(sizeof(T), alignof(T));
            new (dest) T(value);
        }
        // Get read pointer. Size and alignment should match written data.
        FORCE_INLINE void* PrepareRead(std::size_t size, std::size_t alignment);

        // Finish and make buffer space available to writer.
        FORCE_INLINE void FinishRead();

        // Read an element from the buffer.
        template <typename T>
        FORCE_INLINE const T& Read()
        {
            void* src = PrepareRead(sizeof(T), alignof(T));
            return *static_cast<T*>(src);
        }

        // Initialize. Buffer must have required alignment. Size must be a power of two.
        void Initialize(void* buffer, std::size_t size)
        {
            m_Reader = m_Writer = LocalState();
            m_ReaderShared.pos = m_WriterShared.pos = 0;
            m_Reader.buffer = m_Writer.buffer = static_cast<char*>(buffer);
            m_Reader.size = m_Writer.size = m_Writer.end = size;
        }

      private:
        FORCE_INLINE static std::size_t Align(std::size_t pos, std::size_t alignment)
        {
#ifdef RINGBUFFER_DO_NOT_ALIGN
            alignment = 1;
#endif
            return (pos + alignment - 1) & ~(alignment - 1);
        }

        FORCE_INLINE void GetBufferSpaceToWriteTo(std::size_t& pos, std::size_t& end);
        FORCE_INLINE void GetBufferSpaceToReadFrom(std::size_t& pos, std::size_t& end);

        // Writer and reader's local state.
        struct alignas(CACHE_LINE_SIZE) LocalState
        {
            LocalState() : buffer(nullptr), pos(0), end(0), base(0), size(0) {}

            char*       buffer;
            std::size_t pos;
            std::size_t end;
            std::size_t base;
            std::size_t size;
        };

        LocalState m_Writer;
        LocalState m_Reader;

        // Writer and reader's shared positions.
        struct alignas(CACHE_LINE_SIZE) SharedState
        {
            std::atomic<std::size_t> pos;
        };

        SharedState m_WriterShared;
        SharedState m_ReaderShared;
    };
}  // namespace msg
}  // namespace xncc